﻿using System;

using Common.Logging;

namespace Cactussoft.Common.Logging
{
    internal sealed class CommonLoggingLogger : ILogger
    {

        #region .ctors

        public CommonLoggingLogger(ILog log)
        {
            Log = log;
        }

        #endregion

        #region ILogger Members

        public bool IsLogEnabled { get { return (Log != null); } }
        public bool IsDebugEnabled { get { return (IsLogEnabled && Log.IsDebugEnabled); } }
        public bool IsTraceEnabled { get { return (IsLogEnabled && Log.IsTraceEnabled); } }
        public bool IsInfoEnabled { get { return (IsLogEnabled && Log.IsInfoEnabled); } }
        public bool IsWarnEnabled { get { return (IsLogEnabled && Log.IsWarnEnabled); } }
        public bool IsErrorEnabled { get { return (IsLogEnabled && Log.IsErrorEnabled); } }
        public bool IsFatalEnabled { get { return (IsLogEnabled && Log.IsFatalEnabled); } }

        public void Info(string message)
        {
            PerformLoggingOperation(IsInfoEnabled, Log.Info, message);
        }

        public void Info(string messageFormat, params object[] args)
        {
            PerformLoggingOperation(IsInfoEnabled, Log.Info, Log.Error, messageFormat, args);
        }

        public void Info(string message, Exception exception)
        {
            PerformLoggingOperation(IsInfoEnabled, Log.Info, message, exception);
        }

        public void Debug(string message)
        {
            PerformLoggingOperation(IsDebugEnabled, Log.Debug, message);
        }

        public void Debug(string messageFormat, params object[] args)
        {
            PerformLoggingOperation(IsDebugEnabled, Log.Debug, Log.Error, messageFormat, args);
        }

        public void Debug(string message, Exception exception)
        {
            PerformLoggingOperation(IsDebugEnabled, Log.Debug, message, exception);
        }

        public void Trace(string message)
        {
            PerformLoggingOperation(IsTraceEnabled, Log.Trace, message);
        }

        public void Trace(string messageFormat, params object[] args)
        {
            PerformLoggingOperation(IsTraceEnabled, Log.Trace, Log.Error, messageFormat, args);
        }

        public void Trace(string message, Exception exception)
        {
            PerformLoggingOperation(IsTraceEnabled, Log.Trace, message, exception);
        }

        public void Warn(string message)
        {
            PerformLoggingOperation(IsWarnEnabled, Log.Warn, message);
        }

        public void Warn(string messageFormat, params object[] args)
        {
            PerformLoggingOperation(IsWarnEnabled, Log.Warn, Log.Error, messageFormat, args);
        }

        public void Warn(string message, Exception exception)
        {
            PerformLoggingOperation(IsWarnEnabled, Log.Warn, message, exception);
        }

        public void Error(string message)
        {
            PerformLoggingOperation(IsErrorEnabled, Log.Error, message);
        }

        public void Error(string messageFormat, params object[] args)
        {
            PerformLoggingOperation(IsErrorEnabled, Log.Error, Log.Error, messageFormat, args);
        }

        public void Error(string message, Exception exception)
        {
            PerformLoggingOperation(IsErrorEnabled, Log.Error, message, exception);
        }

        public void Error(Exception exception)
        {
            PerformLoggingOperation(IsErrorEnabled, Log.Error, exception);
        }

        public void Fatal(string message)
        {
            PerformLoggingOperation(IsFatalEnabled, Log.Fatal, message);
        }

        public void Fatal(string messageFormat, params object[] args)
        {
            PerformLoggingOperation(IsFatalEnabled, Log.Fatal, Log.Error, messageFormat, args);
        }

        public void Fatal(string message, Exception exception)
        {
            PerformLoggingOperation(IsFatalEnabled, Log.Fatal, message, exception);
        }

        public void Fatal(Exception exception)
        {
            PerformLoggingOperation(IsFatalEnabled, Log.Fatal, exception);
        }

        #endregion

        private delegate void LoggingDelegate(string message);
        private delegate void LoggingMessageWithExceptionDelegate(string message, Exception exception);
        private delegate void LoggingExceptionDelegate(Exception exception);

        #region Properties

        private ILog Log { get; set; }

        #endregion

        #region Private static (utility) methods

        private static void PerformLoggingOperation(
            bool isOperationEnabled,
            LoggingDelegate invocationTarget,
            string message)
        {
            if (isOperationEnabled)
            {
                invocationTarget(message);
            }
        }

        private static void PerformLoggingOperation(
            bool isOperationEnabled,
            LoggingDelegate invocationTarget,
            LoggingMessageWithExceptionDelegate loggingFailedInvocationTarget,
            string messageFormat, params object[] args)
        {
            try
            {
                if (isOperationEnabled)
                {
                    invocationTarget(messageFormat.FormatWithInvariant(args));
                }
            }
            catch (ArgumentException ex)
            {
                loggingFailedInvocationTarget("Internal logging error!", ex);
            }
        }

        private static void PerformLoggingOperation(
            bool isOperationEnabled,
            LoggingMessageWithExceptionDelegate invocationTarget,
            string message, Exception exception)
        {
            if (isOperationEnabled)
            {
                invocationTarget(message, exception);
            }
        }

        private static void PerformLoggingOperation(
            bool isOperationEnabled,
            LoggingExceptionDelegate invocationTarget,
            Exception exception)
        {
            if (isOperationEnabled)
            {
                invocationTarget(exception);
            }
        }

        #endregion
    }
}
